using System;
using System.Collections.Generic;
using UnityEngine;

namespace Framework.FSMLite
{
    public class StateMachineRunner<TState> where TState : Enum
    {

        public TState LastState { get; private set; }

        /// <summary>
        /// 当前状态
        /// </summary>
        public TState CurrentState { get; private set; }

        /// <summary>
        /// 是否已激活状态机
        /// </summary>
        public bool IsActivated { get; private set; }

        private Dictionary<TState, StateMachine<TState>> _actionNodes;

        public event Action<TState>         OnStateEnter;
        public event Action<TState>         OnStateExit;
        public event Action<TState, TState> OnStateChange;

        public StateMachineRunner()
        {
            this._actionNodes = new Dictionary<TState, StateMachine<TState>>();
            this.IsActivated  = false;
            this.OnInit();
        }

        public virtual void OnInit() { }

        /// <summary>
        /// 向状态机中增加状态（反射创建）, 如果设置Unity的代码剥离等级大于low，必须要将待使用的节点增加到剥离白名单link中
        /// 或者使用AppendState(TState state , StateMachine<TState> actionNode) 直接添加对象
        /// </summary>
        /// <param name="state"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public StateMachineRunner<TState> AppendState<T>(TState state) where T : StateMachine<TState>
        {
            Type t          = typeof(T);
            var  actionNode = (StateMachine<TState>) Activator.CreateInstance(t);
            if (!this._actionNodes.ContainsKey(state))
            {
                this._actionNodes.Add(state, actionNode);
                actionNode.InitNode(this, state);
            }
            else
            {
                Debug.LogError("重复添加状态机节点 : " + state);
            }

            return this;
        }
        
        /// <summary>
        /// 向状态机中增加状态
        /// </summary>
        /// <param name="state"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public StateMachineRunner<TState> AppendState(TState state , StateMachine<TState> actionNode)  
        {
            if (!this._actionNodes.ContainsKey(state))
            {
                this._actionNodes.Add(state, actionNode);
                actionNode.InitNode(this, state);
            }
            else
            {
                Debug.LogError("重复添加状态机节点 : " + state);
            }
            return this;
        }

        /// <summary>
        /// 激活状态机
        /// </summary>
        /// <param name="state"></param>
        public void Active(TState state, params object[] args)
        {
            if (this.IsActivated)
            {
                Debug.LogError("状态机已激活");
                return;
            }

            if (this._actionNodes.ContainsKey(state) == false)
                throw new Exception("无法激活状态机，因为该状态机没有指定状态： " + state);
            this.IsActivated  = true;
            this.LastState    = state;
            this.CurrentState = state;
            this.OnStateEnter?.Invoke(this.CurrentState);
            this._actionNodes[this.CurrentState]._OnEnter(args);
        }

        public void Kill() 
        {
            if (this.IsActivated)
            {
                this.OnStateExit?.Invoke(this.CurrentState);
                this._actionNodes[this.CurrentState]._OnExit();
                this._actionNodes.Clear();
                this.IsActivated = false;
            }
        }


        /// <summary>
        /// 切换状态机
        /// </summary>
        /// <param name="state"></param>
        public void OpenState(TState state, params object[] args)
        {
            if (!this.IsActivated)
            {
#if UNITY_EDITOR
                Debug.LogError("当前状态机未激活，无法切换状态");
#endif
                return;
            }

            if (this.CurrentState.Equals(state))
            {
#if UNITY_EDITOR
                Debug.LogWarning("当前处在指定状态中，不能重复激活");
#endif
                return;
            }
#if UNITY_EDITOR
            Debug.Log($"{this.GetType().Name} 状态切换：{this.CurrentState} => {state}");
#endif
            this.LastState    = this.CurrentState;
            this.CurrentState = state;
            this.OnStateChange?.Invoke(this.LastState, this.CurrentState);
            this.OnStateExit?.Invoke(this.LastState);
            this._actionNodes[this.LastState]._OnExit();
            this.OnStateEnter?.Invoke(this.CurrentState);
            this._actionNodes[this.CurrentState]._OnEnter(args);
        }

        public T GetCurStateMachine<T>() where T : StateMachine<TState>
        {
            if (this.IsActivated)
            {
                return (T)this._actionNodes[this.CurrentState];
            }
            return null;
        }

    }
}